﻿using Bogus;
using Microsoft.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace ConsoleApp.Job.TaskJob
{
    public class 生成产品数据任务 : Infrastructure.TaskFramework.TaskBase
    {
        public override async Task ExecuteAsync()
        {
            try
            {
                var context = DataContextUtil.GetDbContext();

                // delete old data  
                await context.Database.ExecuteSqlRawAsync("truncate product;");

                // setup bogus faker  
                var faker = new Faker<Product>("zh_CN");
                faker.RuleFor(p => p.Code, f => f.Commerce.Ean13());
                faker.RuleFor(p => p.Address, f => f.Address.FullAddress());
                faker.RuleFor(p => p.Description, f => f.Commerce.ProductName());
                faker.RuleFor(p => p.Category, f => f.Commerce.Categories(1)[0]);
                faker.RuleFor(p => p.Price, f => f.Random.Decimal(1, 1000));

                // generate 1 million products  
                var products = faker.Generate(1000000);

                var batches = products
                    .Select((p, i) => (Product: p, Index: i))
                    .GroupBy(x => x.Index / 100000)
                    .Select(g => g.Select(x => x.Product).ToList())
                    .ToList();

                // insert batches  
                var stopwatch = new Stopwatch();
                stopwatch.Start();

                var count = 0;
                foreach (var batch in batches)
                {
                    count++;
                    Console.WriteLine($"Inserting batch {count} of {batches.Count}...");

                    await context.Products.AddRangeAsync(batch);
                    await context.SaveChangesAsync();
                }

                stopwatch.Stop();

                Console.WriteLine($"Elapsed time: {stopwatch.Elapsed}");
                Console.WriteLine("Press any key to exit...");
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
    }

    public class 生成用户数据任务 : Infrastructure.TaskFramework.TaskBase
    {
        public override async Task ExecuteAsync()
        {
            var context = DataContextUtil.GetDbContext();

            try
            {
                // delete old data  
                await context.Database.ExecuteSqlRawAsync("truncate user;");

                // setup bogus faker  
                var cities = new[] { "武汉", "福州", "泉州", "深圳", "北京", "上海", "广州", "厦门", "成都", "重庆" };
                var faker = new Faker<SysUser>("zh_CN")
                    .RuleFor(s => s.Name, f => f.Name.LastName() + f.Name.FirstName())
                    .RuleFor(s => s.Age, f => f.Random.Int(18, 45))
                    .RuleFor(s => s.Major, f => f.Commerce.Department())
                    .RuleFor(s => s.Home, f => f.PickRandom((cities)))
                    .RuleFor(p => p.Address, f => f.Address.FullAddress())
                    .RuleFor(c => c.City, f => f.Address.City())
                    .RuleFor(c => c.Country, f => f.Address.Country())
                    .RuleFor(c => c.Latitude, f => f.Address.Latitude().ToString())
                    .RuleFor(c => c.Longitude, f => f.Address.Longitude().ToString())
                    .RuleFor(c => c.ZipCode, f => f.Address.ZipCode())
                    .RuleFor(c => c.Phone, f => f.Phone.PhoneNumber())
                    .RuleFor(c => c.Email, f => f.Internet.Email())
                    .RuleFor(o => o.Shipped, f => f.Random.Bool(0.9f))
                    .RuleFor(u => u.Created, f => f.Date.RecentOffset());

                // generate 1 million users  
                var users = faker.Generate(1000);

                var batches = users
                    .Select((p, i) => (SysUser: p, Index: i))
                    .GroupBy(x => x.Index / 100)
                    .Select(g => g.Select(x => x.SysUser).ToList())
                    .ToList();

                // insert batches  
                var stopwatch = new Stopwatch();
                stopwatch.Start();

                var count = 0;
                foreach (var batch in batches)
                {
                    count++;
                    Console.WriteLine($"Inserting batch {count} of {batches.Count}...");

                    await context.SysUsers.AddRangeAsync(batch);
                    await context.SaveChangesAsync();
                }

                stopwatch.Stop();

                Console.WriteLine($"Elapsed time: {stopwatch.Elapsed}");
                Console.WriteLine("Press any key to exit...");
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }

        }
    }

    [Table("user")]
    public class SysUser
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public string Major { get; set; }
        public string Home { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string Country { get; set; }
        public string ZipCode { get; set; }
        public string Phone { get; set; }
        public string Email { get; set; }
        public bool Shipped { get; set; }
        public string Latitude { get; set; }
        public string Longitude { get; set; }
        public DateTimeOffset? Created { get; set; }
    }

    [Table("product")]
    public class Product
    {
        public int Id { get; set; }
        public string Code { get; set; }
        public string Description { get; set; }
        public string Category { get; set; }
        public string Address { get; set; }
        public decimal Price { get; set; }
    }
    
    public class DataContextUtil
    {
        public static DataContext GetDbContext()
        {
            // initialize data context  
            var connectionString = "server=localhost;port=3306;uid=root;pwd=root;database=test";
            var contextOptionsBuilder = new DbContextOptionsBuilder<DataContext>();
            var serverVersion = ServerVersion.AutoDetect(connectionString);
            contextOptionsBuilder.UseMySql(connectionString, serverVersion);
            var context = new DataContext(contextOptionsBuilder.Options);
            return context;
        }
    }

    public class DataContext : DbContext
    {
        public DataContext(DbContextOptions<DataContext> options) : base(options)
        {
        }

        public DbSet<Product> Products { get; set; }

        public DbSet<SysUser> SysUsers { get; set; }
    }
}
